﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Web;
using QuailServices.QuailDataServiceReference;
using System.Linq;
using System.Reflection;

namespace Quail
{
    /// <summary>
    /// Extensions for the Task class
    /// </summary>
    public static class TaskExtensions
    {
        /// <summary>
        /// Returns the ID of the test run associated with the logger for the supplied task
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        public static int TestRunId(this Task task)
        {
            int result = 0;

			var irunid = task.TaskImplementors.FirstOrDefault(t => t is ITestRunId) as ITestRunId;
            if (irunid != null)
            {
                result = irunid.GetTestRunId(30000);
            }

            return result;
        }

        /// <summary>
        /// Returns the ID of the test run task associated with the logger for the supplied task
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        public static long TestRunTaskId(this Task task)
        {
            long result = 0;

			var irunid = task.TaskImplementors.FirstOrDefault(t => t is ITestRunId) as ITestRunId;
            if (irunid != null)
            {
                result = irunid.GetTestRunTaskId(30000);
            }

            return result;
        }
    }

    /// <summary>
    /// The Task class
    /// </summary>
    public sealed class Task : IDisposable
    {
        private static int _indexCounter;
        private readonly List<Task> _children = new List<Task>();
        private readonly List<ITaskImplementor> _taskImplementors = new List<ITaskImplementor>();
        private readonly int _threadId;
        private LogStatus _overallStatus = LogStatus.Pass;
        private bool _isActive, _taskImplementorsLoaded;
        private TaskAssert _assert;
        private LogStatus? _rollBackStatus;
        private bool _disposed;

        /// <summary>
        /// Initializes a new instance of the <see cref="Task"/> class.
        /// </summary>
        /// <param name="logger">The test logger</param>
        /// <param name="testsession">The test session.</param>
        /// <param name="parentTask">The parent Task.</param>
        /// <param name="name">The name of the task</param>
        public Task(TestLogger logger, TestSession testsession, Task parentTask, string name)
        {
            _threadId = Thread.CurrentThread.ManagedThreadId;
            TestSession = testsession;
            Logger = logger;
            Name = name;
            Index = Interlocked.Increment(ref _indexCounter);
            _isActive = true;
            ParentTask = parentTask;

            if (ParentTask == null)
                // root task resets the ThreadState
                ThreadState = LogStatus.Invalid;
            else
            {
                lock (ParentTask._children)
                {
                    ParentTask._children.Add(this);
                }

                lock (ParentTask._taskImplementors)
                {
                    foreach (var impl in ParentTask.TaskImplementors)
                        _taskImplementors.Add(impl.BeginNewTaskImpl(testsession, name));
                }

                _taskImplementorsLoaded = true;
            }
        }

        ~Task()
        {
            if (_isActive)
                Finish();
        }

        /// <summary>
        /// Gets the task assertion instance.
        /// </summary>
        public TaskAssert Assert

        {
            get
            {
                if (_assert == null)
                    _assert = new TaskAssert(this);

                return _assert;
            }
        }

        /// <summary>
        /// Gets the index of the task
        /// </summary>
        public int Index { get; private set; }

        /// <summary>
        /// Gets the parent task for this task
        /// </summary>
        public Task ParentTask
        {
            get; private set;
        }

        /// <summary>
        /// Gets or sets the status to use for calls to RollBack
        /// </summary>
        public LogStatus RollbackStatus
        {
            get
            {
                if (_rollBackStatus.HasValue)
                    return _rollBackStatus.Value;

                if (ParentTask == null)
                    return _rollBackStatus.GetValueOrDefault(LogStatus.Rollback);

                return ParentTask.RollbackStatus;
            }

            set
            {
                _rollBackStatus = value;
            }
        }

        /// <summary>
        /// Gets an array of the children of this task
        /// </summary>
        public Task[] Children
        {
            get
            {
                lock (_children)
                    return _children.ToArray();
            }
        }

        /// <summary>
        /// Gets a value indicating whether task is the root task
        /// </summary>
        public bool IsRoot
        {
            get
            {
                return ParentTask == null;
            }
        }

        /// <summary>
        /// Gets a value indicating whether task is active
        /// </summary>
        public bool IsActive
        {
            get
            {
                return _isActive;
            }
        }

        /// <summary>
        /// Gets Name.
        /// </summary>
        public string Name
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the ThreadState, used by ParallelTestFixture
        /// </summary>
        [ThreadStatic] internal static LogStatus ThreadState;

        /// <summary>
        /// Gets or sets the OverallState of the test Pass/Warning/Fail
        /// </summary>
        public LogStatus OverallStatus
        {
            get
            {
                return _overallStatus;
            }

            set
            {
                switch (value)
                {
                    case LogStatus.Pass:
                    case LogStatus.Information:
                        break;
                    case LogStatus.Warning:
                        if (!_overallStatus.IsFailingStatus() && _overallStatus != LogStatus.Abandoned)
                            _overallStatus = value;
                        break;
                    case LogStatus.Abandoned:
                        // abandoned status always wins
                        _overallStatus = value;
                        break;
                    default:
                        // the first failing task will always win the overall status
                        if (!_overallStatus.IsFailingStatus() && _overallStatus != LogStatus.Abandoned)
                            _overallStatus = value;
                        break;
                }
            }
        }

        /// <summary>
        /// Gets the active task
        /// </summary>
        public Task ActiveTask
        {
            get
            {
                lock (_children)
                {
                    if (_children.Count == 0)
                        return this;

                    var mythreadid = Thread.CurrentThread.ManagedThreadId;

                    for (var childidx = _children.Count - 1; childidx >= 0; childidx--)
                    {
                        var child = _children[childidx];
                        if (child._threadId == mythreadid)
                            return child.ActiveTask;
                    }

                    return this;
                }
            }
        }

        /// <summary>
        /// Gets the test session
        /// </summary>
        public TestSession TestSession
        {
            get; private set;
        }

        /// <summary>
        /// Gets the test Logger
        /// </summary>
        public TestLogger Logger
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the LogStatus to use in calls to Finish()
        /// </summary>
        private LogStatus FinishState
        {
            get
            {
                if (ParentTask == null)
                    return _overallStatus;

                switch (_overallStatus)
                {
                    case LogStatus.FailYsod:
                        return LogStatus.FailYsod;
                    case LogStatus.Fail:
                        return LogStatus.Fail;
                    case LogStatus.Warning:
                        return LogStatus.Warning;
                    default:
                        return LogStatus.Pass;
                }
            }
        }

        /// <summary>
        /// Returns a list of the TaskImplementors for the Task
        /// </summary>
        public List<ITaskImplementor> TaskImplementors
        {
            get
            {
                if (!_taskImplementorsLoaded)
                {
                    DelayLoadImplementors();
                    _taskImplementorsLoaded = true;
                }

                return _taskImplementors;
            }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            if (!_disposed)
            {
                if (_isActive)
                    Finish();

                if (IsRoot)
                    Logger.Dispose();

                foreach (var ti in _taskImplementors)
                    ti.Dispose();

                _taskImplementors.Clear();
                
                _disposed = true;
            }
        }

        /// <summary>
        /// Converts a message + parameters using string.Format
        /// </summary>
        /// <param name="message">The string message.</param>
        /// <param name="values">The optional values.</param>
        /// <returns>A formatted string</returns>
        public static string ParamsToString(string message, params object[] values)
        {
            return (values == null || values.GetUpperBound(0) == -1) ? message : string.Format(message, values);
        }

        /// <summary>
        /// Begins a task using the calling method name for the name of the Task
        /// Use the return value in a call to Finish, AddTaskAttribute, or RollbackTask
        /// </summary>
        /// <returns>Task object to be used when the task finishes</returns>
        public Task BeginTask()
        {
            StackTrace stackTrace = new StackTrace();
            StackFrame stackFrame = stackTrace.GetFrame(1);
            MethodBase methodBase = stackFrame.GetMethod();

            return BeginTask(methodBase.Name);
        }

        /// <summary>
        /// Begins a task.  Use the return value in a call to Finish, AddTaskAttribute, or RollbackTask
        /// </summary>
        /// <param name="taskname">Name of the task</param>
        /// <returns>Task object to be used when the task finishes</returns>
        public Task BeginTask(string taskname)
        {
            return BeginTask(taskname, string.Empty);
        }

        /// <summary>
        /// Begins a task.  Use the return value in a call to Finish, AddTaskAttribute, or RollbackTask
        /// </summary>
        /// <param name="taskname">Name of the task</param>
        /// <param name="message">A text message to include with the task</param>
        /// <param name="values">Optional values for the message</param>
        /// <returns>Task object to be used when the task finishes</returns>
        public Task BeginTask(string taskname, string message, params object[] values)
        {
            return BeginTask(taskname, ParamsToString(message, values));
        }

        /// <summary>
        /// Begins a task.  Use the return value in a call to Finish, AddTaskAttribute, or RollbackTask
        /// </summary>
        /// <param name="taskname">Name of the task</param>
        /// <param name="message">A text message to include with the task</param>
        /// <returns>Task object to be used when the task finishes</returns>
        public Task BeginTask(string taskname, string message)
        {
            var task = BeginTask(Logger, TestSession, this, taskname);
            if (!string.IsNullOrEmpty(message))
                task.AddLog(message);

            return task;
        }

        /// <summary>
        /// Begins a task.  Use the return value in a call to Finish, AddTaskAttribute, or RollbackTask
        /// </summary>
        /// <param name="logger">The owning logger</param>
        /// <param name="session">The test session</param>
        /// <param name="parenttask">The parent task object</param>
        /// <param name="taskname">Name of the task</param>
        /// <returns>Task object to be used when the task finishes</returns>
        public static Task BeginTask(TestLogger logger, TestSession session, Task parenttask, string taskname)
        {
            return new Task(logger, session, parenttask, taskname);    
        }

        /// <summary>
        /// Encapsulates the usual try/catch and finish calls when running a task, making devs write less code.  You are welcome to call task.Finish() with your own result.
        /// This overload will use the calling method name as the taskname.  This implementation allows a return type T.
        /// </summary>
        /// <param name="func">The function to run within the context of the task</param>
        /// <param name="logResult">True if you want to log the result of the task during the call to Finish()</param>
        [DebuggerStepThrough]
        public T RunTask<T>(Func<Task, T> func, bool logResult = true)
        {
            return RunTask(StackHelper.GetCallingMethodName(true, 1), func, logResult);
        }

        /// <summary>
        /// Encapsulates the usual try/catch and finish calls when running a task, making devs write less code.  You are welcome to call task.Finish() with your own result.
        /// This overload will use the calling method name as the taskname.
        /// </summary>
        /// <param name="action">The <see cref="Action"/> to run within the context of the task</param>
        [DebuggerStepThrough]
        public void RunTask(Action<Task> action)
        {
            RunTask(StackHelper.GetCallingMethodName(true, 1), action);
        }

        /// <summary>
        /// Encapsulates the usual try/catch and finish calls when running a task, making devs write less code.  You are welcome to call task.Finish() with your own result.
        /// </summary>
        /// <param name="taskname">Name of the task</param>
        /// <param name="action">The <see cref="Action"/> to run within the context of the task</param>
        [DebuggerStepThrough]
        public void RunTask(string taskname, Action<Task> action)
        {
            RunTask(taskname,
                    task =>
                        {
                            action(task);
                            return 0;
                        },
                    false);
        }

        /// <summary>
        /// Encapsulates the usual try/catch and finish calls when running a task, making devs write less code.  You are welcome to call task.Finish() with your own result.
        /// </summary>
        /// <param name="taskCreate">Delegate to create the task</param>
        /// <param name="action">The <see cref="Action"/> to run within the context of the task</param>
        [DebuggerStepThrough]
        public void RunTask(Func<Task, Task> taskCreate, Action<Task> action)
        {
            RunTask(taskCreate,
                    task =>
                        {
                            action(task);
                            return 0;
                        },
                    false);
        }

        /// <summary>
        /// Encapsulates the usual try/catch and finish calls when running a task, making devs write less code.  You are welcome to call task.Finish() with your own result.
        /// This implementation allows a return type T.
        /// </summary>
        /// <param name="taskname">Name of the task</param>
        /// <param name="func">The function to run within the context of the task</param>
        /// <param name="logResult">True if you want to log the result of the task during the call to Finish()</param>
        [DebuggerStepThrough]
        public T RunTask<T>(string taskname, Func<Task, T> func, bool logResult = true)
        {
            return RunTask(t => t.BeginTask(taskname), func, logResult);
        }

        /// <summary>
        /// Encapsulates the usual try/catch and finish calls when running a task, making devs write less code.  You are welcome to call task.Finish() with your own result.
        /// This implementation allows a return type T.
        /// </summary>
        /// <param name="taskCreate">Delegate to create the task</param>
        /// <param name="func">The function to run within the context of the task</param>
        /// <param name="logResult">True if you want to log the result of the task during the call to Finish()</param>
        [DebuggerStepThrough]
        public T RunTask<T>(Func<Task, Task> taskCreate, Func<Task, T> func, bool logResult = true)
        {
            var subtask = taskCreate(this);
            try
            {
                var r = func(subtask);

                if (subtask.IsActive)
                {
                    if (logResult)
                        subtask.Finish(r);
                    else
                        subtask.Finish();
                }

                return r;
            }
            catch (Exception e)
            {
                if (subtask.IsActive)
                    subtask.Finish(e);
                throw;
            }
        }


        /// <summary>
        /// Adds an informational message to the log
        /// </summary>
        /// <param name="message">A message for the log entry</param>
        /// <param name="values">Parameters to use in a call to string.Format</param>
        public void AddLog(string message, params object[] values)
        {
            AddLog(ParamsToString(message, values));
        }

        /// <summary>
        /// Adds an informational message to the log
        /// </summary>
        /// <param name="message">A message for the log entry</param>
        public void AddLog(string message)
        {
            AddLog(LogStatus.Information, message);
        }

        /// <summary>
        /// Adds a message to the log
        /// </summary>
        /// <param name="state">What type of message it should be</param>
        /// <param name="message">A text message to include with the task</param>
        /// <param name="values">Parameters to use in a call to string.Format</param>
        public void AddLog(LogStatus state, string message, params object[] values)
        {
            AddLog(state, ParamsToString(message, values));
        }

        /// <summary>
        /// Adds a log entry with the given state and no message
        /// </summary>
        /// <param name="state">The log status for the entry</param>
        public void AddLog(LogStatus state)
        {
            AddLog(state, string.Empty);
        }

        /// <summary>
        /// Adds a log entry with the given state and message
        /// </summary>
        /// <param name="state">The log status for the entry</param>
        /// <param name="message">A message for the log entry</param>
        public void AddLog(LogStatus state, string message)
        {
            AddLogAndUrl(state, message, TestSession.CurrentUrl);
        }

        /// <summary>
        /// Adds a log entry with the given state and message
        /// </summary>
        /// <param name="state">The log status for the entry</param>
        /// <param name="message">A message for the log entry</param>
        /// <param name="url">the url</param>
        public void AddLogAndUrl(LogStatus state, string message, string url)
        {
            OverallStatus = state;

            foreach (var impl in TaskImplementors)
                impl.AddImpl(state, message, url);
            Logger.PerformAutoFlush();
        }

        /// <summary>
        /// Log the object
        /// </summary>
        /// <param name="obj">the object to log</param>
        public void AddLog(object obj)
        {
            if (obj != null)
                AddLog(obj.ToString());
        }

        /// <summary>
        /// Logs an exception
        /// </summary>
        /// <param name="status">the status of the exception</param>
        /// <param name="ex">the exception</param>
        public void AddLog(LogStatus status, Exception ex)
        {
            AddLog(status, FormatException(ex, 0));
        }

        /// <summary>
        /// Logs an exception using the default log status
        /// </summary>
        /// <param name="ex">the exception</param>
        public void AddLog(Exception ex)
        {
            AddLog(GetExceptionLogStatus(ex), FormatException(ex, 0));
        }

        /// <summary>
        /// Adds a monitor log, used by the monitoring system
        /// </summary>
        /// <param name="status">The log status</param>
        /// <param name="scenario">The scenario</param>
        /// <param name="message"></param>
        public void AddMonitorLog(LogStatus status, Enum scenario, string message)
        {
            OverallStatus = status;

            foreach (var impl in TaskImplementors)
                impl.StoreMonitorImpl(status, scenario, message);
            Logger.PerformAutoFlush();
        }
        /// <summary>
        /// Performs a screenshot
        /// </summary>
        /// <param name="state">The log status for the entry</param>
        /// <param name="message">A message for the log entry</param>
        /// <param name="values">Parameters to use in a call to string.Format</param>
        public void Screenshot(LogStatus state, string message, params object[] values)
        {
            Screenshot(state, ParamsToString(message, values));
        }

        /// <summary>
        /// Performs a screenshot
        /// </summary>
        /// <param name="state">The log status for the entry</param>
        /// <param name="message">A message for the log entry</param>
        public void Screenshot(LogStatus state, string message)
        {
            try
            {
                var filename = System.IO.Path.GetTempFileName();

                if (TestSession.Browser is WebBrowser)
                    ((WebBrowser)TestSession.Browser).SeleniumInterface.CaptureEntirePageScreenshot(filename, string.Empty);

                OverallStatus = state;

                foreach (var impl in TaskImplementors)
                    impl.ScreenshotImpl(state, filename, message);

                try
                {
                    System.IO.File.Delete(filename);
                }
                catch
                {
                    // ignore any exceptions during delete
                }
            }
            catch (Selenium.SeleniumException se)
            {
                AddLog(state, "Error creating screenshot!  Message: " + se.Message + ".  Original message:" + message);
            }
        }

        /// <summary>
        /// Appends an attribute to the task
        /// </summary>
        /// <param name="attribute">name of the attribute</param>
        /// <param name="message">value of the attribute</param>
        /// <param name="values">optional values for the message</param>
        public void AddAttribute(string attribute, string message, params object[] values)
        {
            foreach (var impl in TaskImplementors)
                impl.AddAttributeImpl(attribute, ParamsToString(message, values));
        }

        /// <summary>
        /// Appends this object to the task attributes
        /// </summary>
        /// <param name="attribute">name of the attribute</param>
        /// <param name="obj">the object to log</param>
        public void AddAttribute(string attribute, object obj)
        {
            AddAttribute(attribute, obj.ToString());
        }

        /// <summary>
        /// Removes the task as if it never occurred
        /// </summary>
        public void Rollback()
        {
            Finish(RollbackStatus, string.Empty);

            //// The code below is to *actually* rollback the task, but for now, we'll just finish the task "rolled back"
            /* if (ParentTask != null)
                ParentTask._children.Remove(this);
            _isActive = false;
            foreach (var impl in TaskImplementors)
                impl.RollbackImpl(); */
        }


        /// <summary>
        /// Completes the task with Pass or Warning
        /// </summary>
        public void Finish()
        {
            Finish(FinishState, string.Empty);
        }

        /// <summary>
        /// Completes the task with Pass or Warning
        /// </summary>
        /// <param name="message">Message to append to the task</param>
        public void Finish(string message)
        {
            Finish(FinishState, message);
        }

        /// <summary>
        /// Completes the task with Pass or Warning
        /// </summary>
        /// <param name="message">Message to append to the task</param>
        /// <param name="values">optional values for the message</param>
        public void Finish(string message, params object[] values)
        {
            Finish(FinishState, ParamsToString(message, values));
        }

        /// <summary>
        /// Completes the task with a Failure and logs the exception message
        /// </summary>
        /// <param name="status">the status of the failure</param>
        /// <param name="exception">The exception</param>
        /// <param name="message">Message to append to the task</param>
        public void Finish(LogStatus status, Exception exception, string message = null)
        {
            AddLog(status,
                   "<h3>Exception: " +
                   HttpUtility.HtmlEncode(exception.Message) +
                   "</h3>\n" +
                   "<table border=1>" + FormatException(exception, 0) + "</table>");

            if (message != null)
                AddLog(status, message);
            
            Finish(status, message);
        }

        private static string FormatException(Exception ex, int depth)
        {
            Func<string, string, string>
                formattr = (caption, value) =>
                           string.Format("<tr><td>{0}</td><td>{1}</td></tr>",
                                         HttpUtility.HtmlEncode(caption),
                                         HttpUtility.HtmlEncode(value ?? string.Empty).Replace("\n", "<br/>"));


            if (ex == null)
                return string.Empty;

            var prefix = string.Empty;
            for (var i = 0; i < depth; i++ )
            {
                prefix += "-";
            }

            if (depth > 0)
                prefix += " ";

            return formattr(prefix + "Message", ex.Message) +
                       formattr(prefix + "Text", ex.ToString()) +
                       FormatException(ex.InnerException, depth + 1);
        }

        private static LogStatus GetExceptionLogStatus(Exception ex)
        {
            if (ex is ILogStatus)
                return ((ILogStatus) ex).LogStatus;

            if (ex is TimeoutException || ex.Message.Contains("timeout"))
                return LogStatus.FailTimeout;

            return LogStatus.Fail;
        }

        /// <summary>
        /// Completes the task with a Failure and logs the exception message
        /// </summary>
        /// <param name="ex">The exception</param>
        /// <param name="message">Message to append to the task</param>
        public void Finish(Exception ex, string message = null)
        {
            Finish(GetExceptionLogStatus(ex), ex, message);
        }

        /// <summary>
        /// Completes the task with a Failure and logs the exception message
        /// </summary>
        /// <param name="ex">The exception</param>
        /// <param name="message">Message to append to the task</param>
        /// <param name="values">optional values for the message</param>
        public void Finish(Exception ex, string message, params object[] values)
        {
            Finish(GetExceptionLogStatus(ex), ex, ParamsToString(message, values));
        }

        /// <summary>
        /// Completes the task with a given state and logs the exception message
        /// </summary>
        /// <param name="state">The status of the task</param>
        /// <param name="message">Message to append to the task</param>
        /// <param name="values">optional values for the message</param>
        public void Finish(LogStatus state, string message, params object[] values)
        {
            Finish(state, ParamsToString(message, values));
        }

        /// <summary>
        /// Completes the task with a given state and logs the exception message
        /// </summary>
        /// <param name="state">The status of the task</param>
        public void Finish(LogStatus state)
        {
            Finish(state, string.Empty);
        }

        /// <summary>
        /// Completes the task
        /// </summary>
        /// <param name="obj">object to be logged to the message log</param>
        public void Finish(object obj)
        {
            if (obj == null)
                AddLog("null");
            else
                AddLog(obj.ToString());
            Finish();
        }

        /// <summary>
        /// Completes the task with a given state and logs the exception message
        /// </summary>
        /// <param name="state">The status of the task</param>
        /// <param name="message">Message to append to the task</param>
        public void Finish(LogStatus state, string message)
        {
            if (!_isActive)
                throw new TaskException("Task.Finish called twice [name:" + Name + "]");

            if (ParentTask != null)
            {
                ParentTask.OverallStatus = state;
                lock (ParentTask._children)
                    ParentTask._children.Remove(this);
            }

            // clear out any abandoned children
            lock (_children)
            {
                foreach (var child in _children.ToArray())
                    child.Finish(LogStatus.Abandoned);
                _children.Clear();
            }

            _isActive = false;
            if (!string.IsNullOrEmpty(message))
                AddLog(state, message);

            foreach (var impl in TaskImplementors)
                impl.FinishImpl(state);

            if (IsRoot)
            {
                foreach (var impl in TaskImplementors)
                    impl.StopImpl(Logger);

                ThreadState = state;
            }

            Logger.PerformAutoFlush();
        }

        /// <summary>
        /// Flushes the task to storage
        /// </summary>
        public void Flush()
        {
            foreach (var impl in TaskImplementors)
                impl.FlushImpl();
        }

        /// <summary>
        /// Stores the network traffic.
        /// </summary>
        /// <param name="url">the url or name for the traffic</param>
        /// <param name="remoteControlUrl">the url or name for the traffic remote control host</param>
        /// <param name="traffic">The traffic.</param>
        public void StoreNetworkTraffic(string url, string remoteControlUrl, int urlCounter, int msUntilReady, List<NetworkTrafficData> traffic)
        {
            foreach (var impl in TaskImplementors)
                impl.StoreNetworkTrafficImpl(url, remoteControlUrl, urlCounter, msUntilReady, traffic);
        }


        private void DelayLoadImplementors()
        {
            foreach (var factory in Logger.TaskFactories)
                _taskImplementors.Add(factory.CreateRootTask(TestSession, Name));
        }
    }
}
